using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Threading.Tasks;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using MySql.Data.MySqlClient;

namespace MT5Trade.Tests.TestFramework
{
    /// <summary>
    /// 统一的测试初始化器
    /// 负责初始化所有测试所需的数据和环境
    /// </summary>
    public class UnifiedTestInitializer
    {
        private readonly IServiceProvider _serviceProvider;
        private readonly IConfiguration _configuration;
        private readonly ILogger<UnifiedTestInitializer> _logger;
        private readonly string _connectionString;
        private static bool _isInitialized = false;
        private static readonly object _initLock = new object();

        public UnifiedTestInitializer(IServiceProvider serviceProvider)
        {
            _serviceProvider = serviceProvider;
            _configuration = serviceProvider.GetRequiredService<IConfiguration>();
            _logger = serviceProvider.GetRequiredService<ILogger<UnifiedTestInitializer>>();

            // 获取连接字符串，如果没有配置则使用默认值
            _connectionString = _configuration.GetConnectionString("TestDatabase")
                ?? "Server=localhost;Database=mt5trade_test;User=root;Password=password;Port=3306;";
        }

        /// <summary>
        /// 初始化所有测试环境
        /// </summary>
        public async Task<bool> InitializeAsync()
        {
            lock (_initLock)
            {
                if (_isInitialized)
                {
                    _logger.LogInformation("测试环境已初始化，跳过重复初始化");
                    return true;
                }
            }

            try
            {
                _logger.LogInformation("========== 开始初始化测试环境 ==========");

                // 1. 检查并创建数据库
                if (!await CheckDatabaseExistsAsync())
                {
                    _logger.LogWarning("数据库不存在，尝试创建...");
                    await CreateDatabaseAsync();
                }

                // 2. 创建表结构
                await CreateTablesAsync();

                // 3. 插入基础测试数据
                await InsertBaseTestDataAsync();

                // 4. 验证初始化
                var success = await VerifyInitializationAsync();

                if (success)
                {
                    lock (_initLock)
                    {
                        _isInitialized = true;
                    }
                    _logger.LogInformation("========== 测试环境初始化成功 ==========");
                }
                else
                {
                    _logger.LogError("测试环境初始化验证失败");
                }

                return success;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "初始化测试环境失败");
                return false;
            }
        }

        /// <summary>
        /// 检查数据库是否存在
        /// </summary>
        private async Task<bool> CheckDatabaseExistsAsync()
        {
            try
            {
                // 使用不带数据库名的连接字符串
                var builder = new MySqlConnectionStringBuilder(_connectionString);
                var databaseName = builder.Database;
                builder.Database = "information_schema";

                using var connection = new MySqlConnection(builder.ConnectionString);
                await connection.OpenAsync();

                var sql = "SELECT COUNT(*) FROM SCHEMATA WHERE SCHEMA_NAME = @dbName";
                using var command = new MySqlCommand(sql, connection);
                command.Parameters.AddWithValue("@dbName", databaseName);

                var count = Convert.ToInt32(await command.ExecuteScalarAsync());
                return count > 0;
            }
            catch (Exception ex)
            {
                _logger.LogWarning($"检查数据库存在性失败: {ex.Message}");
                return false;
            }
        }

        /// <summary>
        /// 创建数据库
        /// </summary>
        private async Task CreateDatabaseAsync()
        {
            var builder = new MySqlConnectionStringBuilder(_connectionString);
            var databaseName = builder.Database;
            builder.Database = "";

            using var connection = new MySqlConnection(builder.ConnectionString);
            await connection.OpenAsync();

            var sql = $"CREATE DATABASE IF NOT EXISTS `{databaseName}` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci";
            using var command = new MySqlCommand(sql, connection);
            await command.ExecuteNonQueryAsync();

            _logger.LogInformation($"数据库 {databaseName} 创建成功");
        }

        /// <summary>
        /// 创建表结构
        /// </summary>
        private async Task CreateTablesAsync()
        {
            _logger.LogInformation("创建测试表结构...");

            // 读取SQL脚本文件
            var scriptPath = Path.Combine(Directory.GetCurrentDirectory(), "Scripts", "CreateTestTables.sql");
            if (!File.Exists(scriptPath))
            {
                _logger.LogWarning($"SQL脚本文件不存在: {scriptPath}，使用内置表结构");
                await CreateTablesInlineAsync();
                return;
            }

            var sqlScript = await File.ReadAllTextAsync(scriptPath);
            await ExecuteSqlScriptAsync(sqlScript);
        }

        /// <summary>
        /// 内置创建表结构（当脚本文件不存在时）
        /// </summary>
        private async Task CreateTablesInlineAsync()
        {
            using var connection = new MySqlConnection(_connectionString);
            await connection.OpenAsync();

            // 创建核心测试表
            var tables = new[]
            {
                @"CREATE TABLE IF NOT EXISTS arbitrage_test_scenarios (
                    id INT AUTO_INCREMENT PRIMARY KEY,
                    scenario_name VARCHAR(100) NOT NULL,
                    scenario_type ENUM('NORMAL', 'BOUNDARY', 'FAILURE') DEFAULT 'NORMAL',
                    main_loss_probability DECIMAL(5,4) NOT NULL,
                    expected_move DECIMAL(10,2) NOT NULL,
                    spread DECIMAL(10,4) NOT NULL,
                    bonus_remaining DECIMAL(10,2) NOT NULL,
                    follower_margin_level DECIMAL(5,2) NOT NULL,
                    volatility DECIMAL(5,4) NOT NULL,
                    expected_gap_min DECIMAL(5,4),
                    expected_gap_max DECIMAL(5,4),
                    should_succeed BOOLEAN DEFAULT TRUE,
                    failure_reason VARCHAR(200),
                    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
                ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4",

                @"CREATE TABLE IF NOT EXISTS test_execution_results (
                    id INT AUTO_INCREMENT PRIMARY KEY,
                    test_class VARCHAR(100) NOT NULL,
                    test_method VARCHAR(100) NOT NULL,
                    test_case_id INT,
                    input_params TEXT,
                    actual_result DECIMAL(20,6),
                    expected_result DECIMAL(20,6),
                    deviation DECIMAL(20,6),
                    passed BOOLEAN NOT NULL,
                    error_message TEXT,
                    execution_time_ms INT,
                    executed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
                    INDEX idx_test_class_method (test_class, test_method),
                    INDEX idx_executed_at (executed_at)
                ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4",

                @"CREATE TABLE IF NOT EXISTS garch_test_data (
                    id INT AUTO_INCREMENT PRIMARY KEY,
                    test_case_name VARCHAR(100) NOT NULL,
                    current_return DECIMAL(10,6) NOT NULL,
                    current_volatility DECIMAL(10,6) NOT NULL,
                    expected_vol_min DECIMAL(10,6) NOT NULL,
                    expected_vol_max DECIMAL(10,6) NOT NULL,
                    garch_omega DECIMAL(10,8) NOT NULL DEFAULT 0.00001,
                    garch_alpha DECIMAL(10,6) NOT NULL DEFAULT 0.05,
                    garch_beta DECIMAL(10,6) NOT NULL DEFAULT 0.94,
                    test_date DATE DEFAULT (CURRENT_DATE),
                    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
                ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4",

                @"CREATE TABLE IF NOT EXISTS var_test_data (
                    id INT AUTO_INCREMENT PRIMARY KEY,
                    test_case_name VARCHAR(100) NOT NULL,
                    entry_price DECIMAL(10,6) NOT NULL,
                    account_equity DECIMAL(15,2) NOT NULL,
                    position_size DECIMAL(10,4) NOT NULL,
                    volatility DECIMAL(10,6) NOT NULL,
                    expected_return DECIMAL(10,6) NOT NULL,
                    confidence_level DECIMAL(5,4) NOT NULL DEFAULT 0.95,
                    max_loss_percentage DECIMAL(5,4) NOT NULL,
                    expected_stop_loss_min DECIMAL(10,6),
                    expected_stop_loss_max DECIMAL(10,6),
                    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
                ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4"
            };

            foreach (var sql in tables)
            {
                using var command = new MySqlCommand(sql, connection);
                await command.ExecuteNonQueryAsync();
            }

            _logger.LogInformation("测试表结构创建成功");
        }

        /// <summary>
        /// 执行SQL脚本
        /// </summary>
        private async Task ExecuteSqlScriptAsync(string script)
        {
            using var connection = new MySqlConnection(_connectionString);
            await connection.OpenAsync();

            // 分割SQL语句
            var statements = script.Split(new[] { ";" }, StringSplitOptions.RemoveEmptyEntries);

            foreach (var statement in statements)
            {
                if (string.IsNullOrWhiteSpace(statement))
                    continue;

                try
                {
                    using var command = new MySqlCommand(statement.Trim(), connection);
                    await command.ExecuteNonQueryAsync();
                }
                catch (Exception ex)
                {
                    _logger.LogWarning($"执行SQL语句失败: {ex.Message}");
                }
            }
        }

        /// <summary>
        /// 插入基础测试数据
        /// </summary>
        private async Task InsertBaseTestDataAsync()
        {
            _logger.LogInformation("插入基础测试数据...");

            using var connection = new MySqlConnection(_connectionString);
            await connection.OpenAsync();

            // 检查是否已有数据
            var checkSql = "SELECT COUNT(*) FROM arbitrage_test_scenarios";
            using var checkCmd = new MySqlCommand(checkSql, connection);
            var count = Convert.ToInt32(await checkCmd.ExecuteScalarAsync());

            if (count > 0)
            {
                _logger.LogInformation($"已存在 {count} 条测试数据，跳过插入");
                return;
            }

            // 插入基础测试场景
            var sql = @"
                INSERT INTO arbitrage_test_scenarios
                (scenario_name, scenario_type, main_loss_probability, expected_move, spread,
                 bonus_remaining, follower_margin_level, volatility, expected_gap_min, expected_gap_max,
                 should_succeed, failure_reason)
                VALUES
                ('标准套利场景', 'NORMAL', 0.70, 50, 2, 400, 2.0, 0.015, 0.07, 0.12, 1, NULL),
                ('高胜率场景', 'NORMAL', 0.75, 60, 2.5, 450, 2.5, 0.018, 0.06, 0.10, 1, NULL),
                ('低波动场景', 'NORMAL', 0.65, 30, 1.2, 300, 1.8, 0.010, 0.08, 0.15, 1, NULL),
                ('边界-最小赠金', 'BOUNDARY', 0.70, 50, 2, 50, 2.0, 0.015, 0.10, 0.20, 1, NULL),
                ('失败-赠金不足', 'FAILURE', 0.70, 50, 2, 0, 2.0, 0.015, NULL, NULL, 0, '赠金余额为零')";

            using var command = new MySqlCommand(sql, connection);
            await command.ExecuteNonQueryAsync();

            _logger.LogInformation("基础测试数据插入成功");
        }

        /// <summary>
        /// 验证初始化
        /// </summary>
        private async Task<bool> VerifyInitializationAsync()
        {
            try
            {
                using var connection = new MySqlConnection(_connectionString);
                await connection.OpenAsync();

                // 验证关键表存在
                var sql = @"
                    SELECT COUNT(*) as table_count
                    FROM information_schema.tables
                    WHERE table_schema = DATABASE()
                      AND table_name IN ('arbitrage_test_scenarios', 'test_execution_results')";

                using var command = new MySqlCommand(sql, connection);
                var count = Convert.ToInt32(await command.ExecuteScalarAsync());

                if (count < 2)
                {
                    _logger.LogError($"关键表不完整，只找到 {count} 个表");
                    return false;
                }

                // 验证有测试数据
                var dataSql = "SELECT COUNT(*) FROM arbitrage_test_scenarios";
                using var dataCmd = new MySqlCommand(dataSql, connection);
                var dataCount = Convert.ToInt32(await dataCmd.ExecuteScalarAsync());

                if (dataCount == 0)
                {
                    _logger.LogError("测试数据为空");
                    return false;
                }

                _logger.LogInformation($"验证通过: 找到 {count} 个表, {dataCount} 条测试数据");
                return true;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "验证初始化失败");
                return false;
            }
        }

        /// <summary>
        /// 清理测试数据
        /// </summary>
        public async Task CleanupAsync()
        {
            try
            {
                _logger.LogInformation("清理测试数据...");

                using var connection = new MySqlConnection(_connectionString);
                await connection.OpenAsync();

                var sql = "DELETE FROM test_execution_results WHERE executed_at < DATE_SUB(NOW(), INTERVAL 7 DAY)";
                using var command = new MySqlCommand(sql, connection);
                var deleted = await command.ExecuteNonQueryAsync();

                _logger.LogInformation($"清理了 {deleted} 条过期测试结果");
            }
            catch (Exception ex)
            {
                _logger.LogWarning($"清理测试数据失败: {ex.Message}");
            }
        }

        /// <summary>
        /// 重置测试环境
        /// </summary>
        public static void Reset()
        {
            lock (_initLock)
            {
                _isInitialized = false;
            }
        }
    }
}